﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;

using RexToy;
using RexToy.Template.Text;

namespace ScriptXCompiler
{
    //Refactor: the whole class -- string const/ more thorough exception msg/layer
    public partial class Translator : SyntaxRewriter
    {
        private int _ident = 4;
        private int _identLevel = 0;
        /// <summary>
        /// when generating static member in a non-static class, need set this flag.
        /// </summary>
        private bool _isStaticMember = false;

        /// <summary>
        /// JsNative interface flag
        /// </summary>
        private bool _isNative = false;
        private readonly SemanticModel _semanticModel;
        private readonly IOutput _output;
        private readonly CodeTemplate _template;
        private readonly TranslateDictionary _dict;
        private ErrorAggregator _aggregator;

        protected int CurrLineIdent
        {
            get { return _identLevel * _ident; }
        }

        public Translator(SemanticModel semanticModel, CodeTemplate template, TranslateDictionary dictionary, IOutput output, ErrorAggregator aggregator)
        {
            this._semanticModel = semanticModel;
            this._output = output;
            this._template = template;
            this._dict = dictionary;
            this._aggregator = aggregator;
        }

        //Note:It must exist, to shortcut visit the using [XXX], otherwise it will visit XXX
        protected override SyntaxNode VisitUsingDirective(UsingDirectiveSyntax node)
        {
            return node;
        }

        protected override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
        {
            string ns = node.Name.GetText();
            if (!RegisteredNamespace.Contains(ns))
            {
                StringTemplate template = _template.NamespaceTemplate;
                template.Assign("namespace", ns, false);
                _output.WriteLineWithIdent(this.CurrLineIdent, template.Render());
                RegisteredNamespace.Add(ns);
            }

            foreach (var member in node.Members)
            {
                if (IsNoCompile(member))
                    continue;

                this.Visit(member);
            }

            return node;
        }

        //Extend:Need configurable Some special class might need to be translate different: e.g.
        //System.Object -> RexToy.Object(Or any other BaseObject in framework)
        //System.IDisposable -> RexToy.IDisposable(Or other)
        //...
        //This can not use attribute, should be configurable
        protected override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
        {
            var ctorInfos = this._semanticModel.GetDeclaredSymbol(node).InstanceConstructors;
            if (ctorInfos.Count > 1)
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.CtorOverload) { SyntaxItem = node };
                _aggregator.AppendIssue(issue);
                return node;
            }

            bool isStatic = false;
            foreach (var modifier in node.Modifiers)
            {
                if (modifier.Kind == SyntaxKind.PartialKeyword)
                {
                    var issue = new CompileIssue(IssueType.Error, IssueId.PartialNotSupport) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                }

                if (modifier.Kind == SyntaxKind.StaticKeyword)
                {
                    isStatic = true;
                    break;
                }
            }
            List<MemberDeclarationSyntax> static_members = new List<MemberDeclarationSyntax>();

            var classInfo = this._semanticModel.GetDeclaredSymbol(node);
            string strClass = this.GetTypeSymbolName(classInfo);

            int memberCount = 0;
            if (_template.StandaloneCtor)
            {
                foreach (var member in node.Members)
                {
                    if (_template.StandaloneCtor && (member is ConstructorDeclarationSyntax))
                    {
                        var ctorSyntax = member as ConstructorDeclarationSyntax;
                        string paramList = this.MakeParametersList(ctorSyntax.ParameterList);
                        StringTemplate beginTemplate = _template.CtorBeginTemplate;
                        beginTemplate.Assign("class", strClass, false);
                        beginTemplate.Assign("param", paramList, false);
                        _output.WriteLineWithIdent(this.CurrLineIdent, beginTemplate.Render());

                        _identLevel++;
                        this.Visit(ctorSyntax.BodyOpt);
                        _identLevel--;

                        StringTemplate endTemplate = _template.CtorEndTemplate;
                        endTemplate.Assign("class", strClass, false);
                        endTemplate.Assign("param", paramList, false);
                        _output.WriteLineWithIdent(this.CurrLineIdent, endTemplate.Render());

                        memberCount++;
                        break;
                    }
                }
            }

            string strBaseClass;
            if (node.BaseListOpt != null)
            {
                var baseInfo = classInfo.BaseType;
                if (!IsScriptSymbol(baseInfo))
                {
                    var issue = new CompileIssue(IssueType.Error, IssueId.UseNonScript, baseInfo);
                    _aggregator.AppendIssue(issue);
                    return node;
                }

                strBaseClass = this.GetTypeSymbolName(baseInfo);
            }
            else
            {
                strBaseClass = "null";
            }

            string strInterfaces = this.MakeInterfacesList(classInfo.Interfaces);

            if (isStatic)
            {
                _output.WriteWithIdent(this.CurrLineIdent, "{0} = ", strClass);
                _output.WriteLine("{");
            }
            else
            {
                StringTemplate template = this._template.ClassBeginTemplate;
                template.Assign("class", strClass, false);
                template.Assign("baseclass", strBaseClass, false);
                template.Assign("interfaces", strInterfaces, false);
                _output.WriteWithIdent(this.CurrLineIdent, template.Render());
            }

            _identLevel++;


            foreach (var member in node.Members)
            {
                var mInfo = _semanticModel.GetDeclaredSymbol(member);

                if (_template.StandaloneCtor && (member is ConstructorDeclarationSyntax))
                {
                    continue;
                }

                if ((!isStatic) && mInfo.IsStatic)
                {
                    static_members.Add(member);
                    memberCount++;
                }
                else
                {
                    this.Visit(member);
                    memberCount++;
                    if (memberCount != node.Members.Count)
                    {
                        _output.WriteLine(",");
                        if (member.Kind == SyntaxKind.MethodDeclaration || member.Kind == SyntaxKind.ConstructorDeclaration || member.Kind == SyntaxKind.PropertyDeclaration)
                        {
                            _output.WriteLine(string.Empty);
                        }
                    }
                }
            }

            _identLevel--;
            _output.WriteLine(string.Empty);

            if (isStatic)
            {
                _output.WriteLineWithIdent(this.CurrLineIdent, "};");
            }
            else
            {
                StringTemplate template = this._template.ClassEndTemplate;
                template.Assign("class", strClass, false);
                template.Assign("baseclass", strBaseClass, false);
                template.Assign("interfaces", strInterfaces, false);
                _output.WriteWithIdent(this.CurrLineIdent, template.Render());
            }

            _output.WriteLine(string.Empty);//Note: Empty line after class declare.

            if (!isStatic && static_members.Count > 0)
            {
                _isStaticMember = true;
                foreach (var member in static_members)
                {
                    _output.WriteWithIdent(this.CurrLineIdent, strClass + ".");
                    Visit(member);
                    _output.WriteLine(";");
                }
                _output.WriteLine(string.Empty);
                _isStaticMember = false;
            }

            return node;
        }

        protected override SyntaxNode VisitFieldDeclaration(FieldDeclarationSyntax node)
        {
            var symbol = _semanticModel.GetDeclaredSymbol(node);
            var info = _semanticModel.GetSemanticInfo(node.Declaration.Type);
            if (!IsScriptSymbol(info.Type))
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.UseNonScript, info.Type);
                _aggregator.AppendIssue(issue);
            }

            int count = 0;
            foreach (var v in node.Declaration.Variables)
            {
                _output.WriteWithIdent(this.CurrLineIdent, GetMemberSymbolName(symbol) + this.GetDeclareOperator());
                if (v.InitializerOpt == null)
                {
                    var typeInfo = node.Declaration.Type;
                    string defaultValue = this.GetDefaultValueOfType(typeInfo);
                    _output.Write(defaultValue);
                }
                else
                {
                    this.Visit(v.InitializerOpt);
                }

                count++;
                if (count != node.Declaration.Variables.Count)
                    _output.WriteLine(",");
            }

            return node;
        }

        protected override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
        {
            var info = _semanticModel.GetSemanticInfo(node.Type);
            if (!IsScriptSymbol(info.Type))
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.UseNonScript, info.Type);
                _aggregator.AppendIssue(issue);
            }

            bool isAuto = false;
            foreach (var a in node.AccessorList.Accessors)
            {
                if (a.BodyOpt == null)
                    isAuto = true;
            }

            if (isAuto)
            {
                string name = "__" + NamingConvention.LowerCase1stChar(node.Identifier.ValueText);
                _output.WriteWithIdent(this.CurrLineIdent, name + this.GetDeclareOperator());

                var typeInfo = node.Type;
                string defaultValue = this.GetDefaultValueOfType(typeInfo);
                _output.WriteLine("{0},", defaultValue);
            }

            int count = 0;
            foreach (var acc in node.AccessorList.Accessors)
            {
                this.VisitAccessorDeclaration(acc);
                count++;
                if (count != node.AccessorList.Accessors.Count)
                {
                    _output.WriteLine(",");
                }
            }

            return node;
        }

        protected override SyntaxNode VisitAccessorDeclaration(AccessorDeclarationSyntax node)
        {
            var info = _semanticModel.GetDeclaredSymbol(node);

            if (info.Name == "get")
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.IndexerNotSupport) { SyntaxItem = node };
                _aggregator.AppendIssue(issue);
            }

            string func_name = info.Name.Remove(info.Name.IndexOf('_'), 1);

            switch (node.Kind)
            {
                case SyntaxKind.GetAccessorDeclaration:
                    _output.WriteWithIdent(this.CurrLineIdent, "{0}{1}function () ", func_name, this.GetDeclareOperator());
                    break;

                case SyntaxKind.SetAccessorDeclaration:
                    _output.WriteWithIdent(this.CurrLineIdent, "{0}{1}function (value) ", func_name, this.GetDeclareOperator());
                    break;

                default:
                    var issue = new CompileIssue(IssueType.Error, IssueId.UnknownAccessor, node.Kind) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                    break;
            }

            _output.WriteLine("{");

            this._identLevel++;
            if (node.BodyOpt == null)
            {
                string field_name = "__" + NamingConvention.LowerCase1stChar(info.Name.RemoveBegin(4));//Note: 4 is get_ or set_
                switch (node.Kind)
                {
                    case SyntaxKind.GetAccessorDeclaration:
                        _output.WriteLineWithIdent(this.CurrLineIdent, "return this.{0};", field_name);
                        break;

                    case SyntaxKind.SetAccessorDeclaration:
                        _output.WriteLineWithIdent(this.CurrLineIdent, "this.{0} = value;", field_name);
                        break;

                    default:
                        var issue = new CompileIssue(IssueType.Error, IssueId.UnknownAccessor, node.Kind) { SyntaxItem = node };
                        _aggregator.AppendIssue(issue);
                        break;
                }
            }
            else
            {
                this.Visit(node.BodyOpt);
            }
            this._identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");
            return node;
        }

        protected override SyntaxNode VisitBlock(BlockSyntax node)
        {
            if (_isNative)
            {
                //Extend:better format
                var txt = node.GetText().UnBracketing(StringPair.CurlyBracket).Trim();
                txt = txt.UnBracketing(StringPair.Create("/*", "*/")).Trim();

                _output.WriteLineWithIdent(this.CurrLineIdent, txt);

                if (node.Statements.Count != 0)
                {
                    CompileIssue issue = new CompileIssue(IssueType.Error, IssueId.NativeFormat) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                }
            }
            else
            {
                foreach (var stmt in node.Statements)
                {
                    this.Visit(stmt);
                    switch (stmt.Kind)
                    {
                        case SyntaxKind.ForEachStatement:
                        case SyntaxKind.ForStatement:
                        case SyntaxKind.IfStatement:
                        case SyntaxKind.TryStatement:
                        case SyntaxKind.WhileStatement:
                        case SyntaxKind.SwitchStatement:
                            _output.WriteLine(String.Empty);
                            break;

                        case SyntaxKind.BreakStatement:
                        case SyntaxKind.ContinueStatement:
                            break;

                        default:
                            _output.WriteLine(";");
                            break;
                    }
                }
            }
            return node;
        }

        protected override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            var info = _semanticModel.GetDeclaredSymbol(node);
            _isNative = IsNativeMethod(info);

            var rInfo = _semanticModel.GetSemanticInfo(node.ReturnType);
            if (!IsScriptSymbol(rInfo.Type))
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.UseNonScript, rInfo.Type);
                _aggregator.AppendIssue(issue);
            }

            _output.WriteWithIdent(this.CurrLineIdent, "{0}{1}function (", GetMemberSymbolName(info), this.GetDeclareOperator());
            _output.Write(this.MakeParametersList(node.ParameterList));
            _output.WriteLine(") {");

            _identLevel++;

            if (node.BodyOpt != null)
                this.VisitBlock(node.BodyOpt);

            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");

            this._isNative = false;
            return node;
        }

        protected override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
        {
            string CTOR = "ctor";
            string constructor = "constructor";
            if (_dict.HasKey(CTOR))
                constructor = _dict.Translate(CTOR);

            _output.WriteWithIdent(this.CurrLineIdent, "{0}: function (", constructor);
            _output.Write(this.MakeParametersList(node.ParameterList));
            _output.WriteLine(") {");

            _identLevel++;
            this.Visit(node.BodyOpt);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");
            return node;
        }

        protected override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
        {
            var info = _semanticModel.GetSemanticInfo(node.Declaration.Type);
            if (!IsScriptSymbol(info.Type))
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.UseNonScript, info.Type);
                _aggregator.AppendIssue(issue);
            }

            _output.WriteWithIdent(this.CurrLineIdent, "var ");
            this.MakeLocalVariableList(node.Declaration);

            return node;
        }

        protected override SyntaxNode VisitForStatement(ForStatementSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, "for (");

            if (node.Initializers != null)
            {
                this.MakeExpressionList(node.Initializers);
            }

            if (node.DeclarationOpt != null)
            {
                _output.Write("var ");
                this.MakeLocalVariableList(node.DeclarationOpt);
            }

            _output.Write("; ");

            this.VisitExpression(node.ConditionOpt);
            _output.Write("; ");

            this.MakeExpressionList(node.Incrementors);

            _output.WriteLine(") {");

            _identLevel++;
            this.Visit(node.Statement);
            this.AppendCompensateSemicolon(node.Statement);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");
            return node;
        }
        protected override SyntaxNode VisitForEachStatement(ForEachStatementSyntax node)
        {
            var info = this._semanticModel.GetSemanticInfo(node.Expression);

            if (this.IsEnumerable(info))
            {
                //Note:Here we use __idx${identifierName} as index
                _output.WriteWithIdent(this.CurrLineIdent, "for (var __idx${0} = 0; __idx${0} < ", node.Identifier.ValueText);
                VisitExpression(node.Expression);
                _output.Write(".length; __idx${0}++) ", node.Identifier.ValueText);
                _output.WriteLine("{");

                _identLevel++;
                _output.WriteWithIdent(this.CurrLineIdent, "var {0} = ", node.Identifier.ValueText);
                Visit(node.Expression);
                _output.WriteLine("[__idx${0}];", node.Identifier.ValueText);

                this.Visit(node.Statement);
                this.AppendCompensateSemicolon(node.Statement);
                _identLevel--;
                _output.WriteWithIdent(this.CurrLineIdent, "}");
            }
            else
            {
                _output.WriteWithIdent(this.CurrLineIdent, "for (var {0} in ", node.Identifier.ValueText);
                VisitExpression(node.Expression);
                _output.WriteLine(") {");

                _identLevel++;
                this.Visit(node.Statement);
                this.AppendCompensateSemicolon(node.Statement);
                _identLevel--;

                _output.WriteWithIdent(this.CurrLineIdent, "}");
            }

            return node;
        }

        protected override SyntaxNode VisitWhileStatement(WhileStatementSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, "while (");
            this.VisitExpression(node.Condition);
            _output.WriteLine(") {");

            _identLevel++;
            this.Visit(node.Statement);
            this.AppendCompensateSemicolon(node.Statement);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");
            return node;
        }

        protected override SyntaxNode VisitDoStatement(DoStatementSyntax node)
        {
            _output.WriteLineWithIdent(this.CurrLineIdent, "do {");

            _identLevel++;
            Visit(node.Statement);
            this.AppendCompensateSemicolon(node.Statement);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "} while (");
            VisitExpression(node.Condition);
            _output.Write(")");
            return node;
        }

        protected override SyntaxNode VisitIfStatement(IfStatementSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, "if ( ");
            this.VisitExpression(node.Condition);
            _output.WriteLine(" ) {");

            _identLevel++;
            this.Visit(node.Statement);
            this.AppendCompensateSemicolon(node.Statement);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");
            if (node.ElseOpt != null)
            {
                _output.WriteLine(" else {");

                _identLevel++;
                this.Visit(node.ElseOpt.Statement);
                this.AppendCompensateSemicolon(node.ElseOpt.Statement);
                _identLevel--;

                _output.WriteWithIdent(this.CurrLineIdent, "}");
            }
            return node;
        }

        protected override SyntaxNode VisitBreakStatement(BreakStatementSyntax node)
        {
            _output.WriteLineWithIdent(this.CurrLineIdent, "break;");
            return node;
        }

        protected override SyntaxNode VisitContinueStatement(ContinueStatementSyntax node)
        {
            _output.WriteLineWithIdent(this.CurrLineIdent, "continue;");
            return node;
        }

        protected override SyntaxNode VisitTryStatement(TryStatementSyntax node)
        {
            _output.WriteLineWithIdent(this.CurrLineIdent, "try {");

            _identLevel++;
            this.Visit(node.Block);
            _identLevel--;

            _output.WriteWithIdent(this.CurrLineIdent, "}");

            if (node.Catches.Count > 0)
            {
                foreach (var c in node.Catches)
                {
                    if (c.DeclarationOpt != null)
                    {
                        var id = c.DeclarationOpt.IdentifierOpt;
                        if (id != null && id.Kind != SyntaxKind.None && id.ValueText != "e")
                        {
                            CompileIssue issue = new CompileIssue(IssueType.Error, IssueId.CatchOnlyE) { SyntaxItem = c };
                            _aggregator.AppendIssue(issue);
                        }
                    }
                }

                _output.WriteLine(" catch (e) {");
                _identLevel++;
                if (node.Catches.Count == 1)
                {
                    this.Visit(node.Catches[0].Block);
                }
                else
                {
                    var count = 0;
                    for (int i = 0; i < node.Catches.Count; i++)
                    {
                        if (count > 0)
                            _output.Write("else ");

                        var c = node.Catches[i];
                        if (c.DeclarationOpt != null)
                        {
                            var info = _semanticModel.GetSemanticInfo(c.DeclarationOpt.Type);
                            var typename = this.GetTypeSymbolName(info.Symbol);

                            if (count == 0)
                                _output.WriteWithIdent(this.CurrLineIdent, "if (e.name == \"{0}\") ", typename);
                            else
                                _output.Write("if (e.name == \"{0}\") ", typename);
                            _output.WriteLine("{");
                            _identLevel++;
                            this.Visit(c.Block);
                            _identLevel--;
                            _output.WriteWithIdent(this.CurrLineIdent, "} ");
                        }
                        else
                        {
                            _output.WriteLine("{");
                            _identLevel++;
                            this.Visit(c.Block);
                            _identLevel--;
                            _output.WriteLineWithIdent(this.CurrLineIdent, "} ");
                        }

                        count++;
                    }
                }
                _identLevel--;
                _output.WriteWithIdent(this.CurrLineIdent, "}");
            }

            if (node.FinallyOpt != null)
            {
                _output.WriteLine(" fianlly {");

                _identLevel++;
                this.Visit(node.FinallyOpt.Block);
                _identLevel--;

                _output.WriteWithIdent(this.CurrLineIdent, "}");
            }

            return node;
        }

        protected override SyntaxNode VisitReturnStatement(ReturnStatementSyntax node)
        {
            if (node.ExpressionOpt != null)
            {
                _output.WriteWithIdent(this.CurrLineIdent, "return ");
                this.VisitExpression(node.ExpressionOpt);
            }
            else
            {
                _output.WriteWithIdent(this.CurrLineIdent, "return");
            }

            return node;
        }

        protected override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, string.Empty);
            this.Visit(node.Expression);
            return node;
        }

        protected override SyntaxNode VisitSwitchStatement(SwitchStatementSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, "switch (");
            this.Visit(node.Expression);
            _output.WriteLine(") {");
            _identLevel++;

            foreach (var section in node.Sections)
            {
                foreach (var lbl in section.Labels)
                {
                    _output.WriteWithIdent(this.CurrLineIdent, lbl.CaseOrDefaultKeyword.ValueText);
                    if (lbl.CaseOrDefaultKeyword.Kind == SyntaxKind.CaseKeyword)
                    {
                        _output.Write(" ");
                        this.VisitExpression(lbl.ValueOpt);
                    }
                    _output.WriteLine(":");
                }

                _identLevel++;
                int count = 0;

                foreach (var stmt in section.Statements)
                {
                    Visit(stmt);
                    var t = stmt.GetLastToken();

                    count++;

                    if (count != section.Statements.Count && t.Kind == SyntaxKind.SemicolonToken)
                        _output.WriteLine(";");

                }
                _identLevel--;
            }

            _identLevel--;
            _output.WriteWithIdent(this.CurrLineIdent, "}");

            return node;
        }

        protected override SyntaxNode VisitEqualsValueClause(EqualsValueClauseSyntax node)
        {
            this.VisitExpression(node.Value);
            return node;
        }

        protected override SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
        {
            if (!_template.SupportInterface)
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.InterfaceNotSupport, _template.Name);
                _aggregator.AppendIssue(issue);
                return node;
            }
            else
            {
                var interfaceInfo = this._semanticModel.GetDeclaredSymbol(node);

                string strInterface = this.GetTypeSymbolName(interfaceInfo);

                StringTemplate begin = this._template.InterfaceBeginTemplate;
                begin.Assign("interface", strInterface, false);
                _output.WriteWithIdent(this.CurrLineIdent, begin.Render());

                _identLevel++;

                var count = 0;
                foreach (var member in node.Members)
                {
                    Visit(member);
                    count++;
                    if (count != node.Members.Count)
                        _output.WriteLine(",");
                }
                _identLevel--;

                StringTemplate end = this._template.InterfaceEndTemplate;
                begin.Assign("interface", strInterface, false);
                _output.WriteWithIdent(this.CurrLineIdent, end.Render());

                if (node.BaseListOpt != null)
                {
                    CompileIssue issue = new CompileIssue(IssueType.Warning, IssueId.InterfaceInheritNotSupport) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                }
            }
            return node;
        }

        protected override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
        {
            if (!_template.SupportEnum)
            {
                var issue = new CompileIssue(IssueType.Error, IssueId.EnumNotSupport, _template.Name);
                _aggregator.AppendIssue(issue);
            }
            else
            {
                var info = _semanticModel.GetDeclaredSymbol(node);
                string enumName = this.GetTypeSymbolName(info);

                StringTemplate beginTemplate = _template.EnumBeginTemplate;
                beginTemplate.Assign("enum", enumName, false);
                _output.WriteWithIdent(this.CurrLineIdent, beginTemplate.Render());

                _identLevel++;
                var count = 0;
                foreach (var member in node.Members)
                {
                    Visit(member);
                    count++;
                    if (count != node.Members.Count)
                    {
                        _output.WriteLine(",");
                    }
                }
                _identLevel--;

                StringTemplate endTemplate = _template.EnumEndTemplate;
                endTemplate.Assign("enum", enumName, false);
                _output.WriteLineWithIdent(this.CurrLineIdent, endTemplate.Render());

                if (node.BaseListOpt != null)
                {
                    CompileIssue issue = new CompileIssue(IssueType.Warning, IssueId.EnumInheritNotSupport) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                }
            }
            return node;
        }

        protected override SyntaxNode VisitEventDeclaration(EventDeclarationSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.EventNotSupport);
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitEventFieldDeclaration(EventFieldDeclarationSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.EventNotSupport);
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitGotoStatement(GotoStatementSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.GotoNotSupport);
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitIndexerDeclaration(IndexerDeclarationSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.IndexerNotSupport) { SyntaxItem = node };
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitLockStatement(LockStatementSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.LockNotSupport);
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitYieldStatement(YieldStatementSyntax node)
        {
            var issue = new CompileIssue(IssueType.Error, IssueId.YieldNotSupport);
            _aggregator.AppendIssue(issue);
            return node;
        }

        protected override SyntaxNode VisitThrowStatement(ThrowStatementSyntax node)
        {
            if (node.ExpressionOpt == null)
            {
                CompileIssue issue = new CompileIssue(IssueType.Error, IssueId.ThrowNothing);
                _aggregator.AppendIssue(issue);
            }
            else
            {
                _output.WriteWithIdent(this.CurrLineIdent, "throw ");
                Visit(node.ExpressionOpt);
            }
            return node;
        }

        protected override SyntaxNode VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node)
        {
            _output.WriteWithIdent(this.CurrLineIdent, node.Identifier.ValueText);
            _output.Write(": ");
            var info = _semanticModel.GetDeclaredSymbol(node);
            string val;
            bool hasAttr = this.GetEnumValue(info, out val);

            if (node.EqualsValueOpt != null)
            {
                if (hasAttr)
                {
                    CompileIssue issue = new CompileIssue(IssueType.Error, IssueId.EnumFieldTwoValue) { SyntaxItem = node };
                    _aggregator.AppendIssue(issue);
                }
                else
                {
                    Visit(node.EqualsValueOpt.Value);
                }
            }
            else
            {
                if (hasAttr)
                {
                    _output.Write(val);
                }
                else
                {
                    CompileIssue issue = new CompileIssue(IssueType.Error, IssueId.EnumFieldNoValue, node);
                    _aggregator.AppendIssue(issue);
                }
            }

            return node;
        }
    }
}
